/*
 Автор: Швейкин Евгений Юрьевич


 Функциональное описание:     
    Модуль содержит фифо, а если в нем есть свободное место, считывает по амм. Если в фифо места
меньше чем на одну пачку данных, чтение останавливается.
    Считывание ппо amm ведется пачками в режиме non stop. Т.е пока в фифо есть место на шину 
выставляются зпросы. Отсчет данных ведется по размеру запросов. В фифо данные записываются по
мере поступления, по readdatavalid.

    Считывание из фифо ведется на сторонней частоте.
    
    На вход модуля подается:
     - количество данных, которое необходимо считать, в 32-хбитных словах;
     - адрес памяти откуда читать, в Байтах;
     - запускающий импульс.
*/
//--------------------------------------------------------------------------------------------------
// Описание структуры порта канала FC

// *********************** НАЧАЛО МОДУЛЯ ***********************************************************
module amm_to_fifo_burst_ns_bw
#(  parameter   
        max_burst_p     = 32,       // максимальный размер пачки
        fifo_size_p     = 1024,     // Размер фифо
        bw_p            = 32        // ширина шины авалон
)
(
    input  logic            reset_n,
    input  logic            clk,
    
    // Управление модулем
    input  logic            start_i,
    input  logic [31:0]     data_size_i,                // Слов
    input  logic [31:0]     base_addr_i,                // Байт
    
    output logic            busy,

    // Порты фифо
    input  logic            data_clk,
    input  logic            rdreq_i,
    
    output logic [31:0]     data_o,
    output logic            rdempty_o,
    output logic [15:0]     rdusedw,
    
    // Сигналы шины master авалон
    input logic             avm_waitrequest,
    input logic [31:0]      avm_readdata,
    input logic             avm_readdatavalid,

    output logic [31:0]     avm_address,
    output logic [9:0]      avm_burstcount,
    output logic            avm_beginbursttransfer,
    output logic            avm_read
);

// ********************** МАКРОСЫ ******************************************************************
    // Описывает процесс модуля выделения фронтов сигнала. 
    // Так же описывает создание новых сигналов с именем <имя сигнала> + <_rise> (<_fall>, <_edge>)
    `define front(clk, name)                    \
    logic name``_0;                             \
    logic name``_1;                             \
    logic name``_rise;                          \
    logic name``_fall;                          \
    logic name``_edge;                          \
    always_ff @(posedge clk)                    \
    begin                                       \
        name``_0 <= name;                       \
        name``_1 <= name``_0;                   \
        name``_rise <= name``_0 & (~name``_1);  \
        name``_fall <= name``_1 & (~name``_0);  \
        name``_edge <= name``_0 ^ name``_1;     \
    end
    
//  ********************* КОНСТАНТЫ ****************************************************************
    localparam fifo_size_lp     = fifo_size_p;
    localparam fifo_wa_lp       = $clog2(fifo_size_lp);
    
//  ********************* ФУНКЦИИ ******************************************************************

//  ********************* ТИПЫ ДАННЫХ **************************************************************
    // Состояния автомата считывания пакетов из фифо и передачи их по Avalon
enum logic [4:0]    {   IDLE_s          = 5'b00001,
                            AMM_REQ_RD_s    = 5'b00010,
                            CALC_s          = 5'b00100,
                            WAIT_s          = 5'b01000,
                            ERR_s           = 5'b10000
                        } state_amm_to_fifo_burst_ns, next_state;
                        
    typedef struct packed 
    {   logic [31:0]                data;
        logic                       wrreq;
        logic [fifo_wa_lp:0]    wrusedw;
        logic                       wrfull;
        
        logic [31:0]                q;
        logic                       rdreq;
        logic [fifo_wa_lp:0]    rdusedw;
        logic                       rdempty;
    } fifo_t;
    
// ********************* ПЕРЕМЕННЫЕ ****************************************************************
    fifo_t                          fifo;
   
    logic                           end_data;
    logic [31:0]                    address;
    
    logic [31:0]                    data_marker;
    logic [31:0]                    data_size_latch;
    logic [31:0]                    fifo_marker;            // Сколько данных не считано по запросам
    
    logic [9:0]                     read_cnt;
    logic                           reset_fifo;
    logic                           fifo_almost_full;
    logic [fifo_wa_lp - 1:0]        wrusedw_latch;
    logic [fifo_wa_lp - 1:0]        wrusedw_sub;
// ********************* ИНИЦИАЛИЗАЦИЯ ПЕРЕМЕННЫХ ДЛЯ СИМУЛЯТОРА ***********************************

    
//  ********************* ASSIGN *******************************************************************
    assign data_o       = fifo.q;
    assign fifo.rdreq   = rdreq_i;
    assign rdempty_o    = fifo.rdempty;
    assign rdusedw      = {{(15-fifo_wa_lp){1'b0}}, fifo.rdusedw};
    
// ********************* ПРОЦЕССЫ ******************************************************************
    `front(clk, start_i)
    
    always_ff @(posedge clk)
    begin
        if (avm_beginbursttransfer)
            read_cnt    <= 10'd0;
        else if (avm_read)
            read_cnt    <= read_cnt + 10'd1;
        else;
    end   

    
   // Переключение состояний автомата (классическое описание)
    always_ff @(posedge clk,  negedge reset_n)
    begin
        if (!reset_n) begin
            state_amm_to_fifo_burst_ns   <= IDLE_s;
        end
        else begin
            state_amm_to_fifo_burst_ns   <= next_state; 
        end
    end
    
    // Определение следующего состояния автомата
    always_comb        
    begin    
        next_state = state_amm_to_fifo_burst_ns; 
        unique case (state_amm_to_fifo_burst_ns)
            IDLE_s:  
            begin
                if (start_i_rise)
                    next_state  = AMM_REQ_RD_s;   
                else;
            end     

            AMM_REQ_RD_s:
            begin                
                next_state  = CALC_s;
            end
            
            CALC_s:
            begin
                next_state  = WAIT_s;
            end
            
            WAIT_s:
            begin
                // Если данные закончились, возвращаем автомат в исходое состояние
                if (end_data)
                    next_state  = IDLE_s;
                // Если фифо не полное, а шина авалон готова, надо сделать еще запрос
                else if ((!fifo_almost_full) && (!avm_waitrequest))
                    next_state  = AMM_REQ_RD_s;
                // Если данные запрошены не все, но новый запрос поставить нельзя, обновим флаги
                else
                    next_state  = CALC_s;
            end
            
            ERR_s:
            begin
                next_state  = IDLE_s;
            end
            
            default:
                next_state  = ERR_s;
        endcase
    end   

    // Счетчик адресов и переключение банков
    always_ff @(posedge clk)
    begin
        // Маркер фифо опускается при вычитке данных
        fifo_marker <= fifo_marker - {31'd0, avm_readdatavalid};
        
        unique case (state_amm_to_fifo_burst_ns)
            IDLE_s:
            begin

                if (start_i) begin
                    data_marker         <= 32'd0;
                    fifo_marker         <= 32'd0;
                    fifo_almost_full    <= 1'b0;
                
                    data_size_latch <= data_size_i;
                    address         <= base_addr_i;
                    end_data        <= 1'b0;
                    avm_burstcount  <= max_burst_p;
                    if (data_size_i < max_burst_p)
                        avm_burstcount  <= data_size_i;
                    else;
                end
                else;
            end
            
            AMM_REQ_RD_s:     
            begin
                // В момент запроса на шине авалон
                if (avm_beginbursttransfer) begin
                    // маркеры поднимается на размер пачки
                    fifo_marker <= fifo_marker + avm_burstcount - {31'd0, avm_readdatavalid};
                    data_marker <= data_marker + avm_burstcount;
                    // avm_burstcount в словах, а адрес считается в байтах
                    address     <= address + (avm_burstcount << 2);
                end
                else;
            end

            CALC_s:
            begin
                // По умолчанию фифо не заполнено
                fifo_almost_full    <= fifo.wrfull;
                // Если в фифо места меньще чем на пачку, значит фифо заполнено
                if ((fifo_marker + fifo.wrusedw) > (fifo_size_lp - max_burst_p - 10)) 
                    fifo_almost_full    <= 1'b1;
                else;
                
                // Если значение маркера не меньше запрошенного размера данных,
                // знчит считаем, что запросили все и ставим флаг конца данных
                if (data_marker >= data_size_latch)
                    end_data <= 1'b1;
                // Если оставшихся не переданных данных меньше пачки, делаем пачку по размеру
                else if  (data_size_latch - data_marker < max_burst_p)
                    avm_burstcount  <= data_size_latch - data_marker;
                else;
                ;
            end

            default: ;
        endcase
    end
    
    // Описание выходов автомата 
    always_comb
    begin
        avm_beginbursttransfer  = 1'b0;
        avm_read                = 1'b0;
        avm_address             = address;
        fifo.wrreq              = avm_readdatavalid;
        fifo.data               = avm_readdata;
        busy                    = 1'b1;
        reset_fifo              = 1'b0;
        unique case (state_amm_to_fifo_burst_ns)
            IDLE_s:  
            begin
                busy    = ~fifo.rdempty;
                reset_fifo  = start_i;
            end     
            
            AMM_REQ_RD_s:     
            begin
                avm_beginbursttransfer  = 1'b1;                 // Запрос
                avm_read                = 1'b1;
            end
            
            default: ;
        endcase
    end
    
// ********************* ПОДКЛЮЧАЕМЫЕ МОДУЛИ *******************************************************
    // Буфер FIFO
    dcfifo  
    #(  .add_usedw_msb_bit      ("ON"),             // разрядность usedw на весь диапазон
        //.intended_device_family ("ARRIA II GX"),  // Arria II GX
        .lpm_showahead          ("ON"),            // режим ASK выключен
        .lpm_type               ("dcfifo"),         
        .overflow_checking      ("ON"),             // Проверка переполнения
        .underflow_checking     ("ON"),             // Проверка опустошения
        .use_eab                ("ON"), 
        .rdsync_delaypipe       (4),                
        .wrsync_delaypipe       (4),
        
        .lpm_numwords           (fifo_size_lp),     // Объем фифо
        .lpm_width              (32),               // Разрядность данных 
        .lpm_widthu             (fifo_wa_lp)        // Разрядность адреса входа
    )
    fifo_inst1
    (   
        .aclr                   (~reset_n | reset_fifo),
    
        // Запись в фифо
        .wrclk                  (clk),    
        .wrreq                  (fifo.wrreq),
        .data                   (fifo.data),
        .wrfull                 (fifo.wrfull),
        .wrusedw                (fifo.wrusedw),
      
        // Чтение из фифо
        .rdclk                  (data_clk),
        .rdreq                  (fifo.rdreq),
        .q                      (fifo.q),
        .rdusedw                (fifo.rdusedw),
        .rdempty                (fifo.rdempty)
    );
endmodule 